package crontab

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"quant/backend/api/dto"
	"quant/backend/constants"
	"quant/backend/db"
	"quant/backend/qmt"
	"quant/backend/utils"
	"strings"

	"github.com/duke-git/lancet/v2/convertor"
	"github.com/duke-git/lancet/v2/mathutil"
	"github.com/redis/go-redis/v9"
	"github.com/sirupsen/logrus"
)

func postMarketJob(strTime string) {
	// 0. 声明一些常用变量
	var err error

	// 1. 校验输入时间格式
	if strTime == "" {
		logrus.Error("预处理任务失败：日期参数为空")
		return
	}

	// 2. 获取沪深A股的所有股票代码列表
	var codeList []string
	if codeList, err = getStockCodeListBySector(SectorAStock); err != nil {
		logrus.Errorf("获取股票代码列表失败: %v", err)
		return
	}

	if len(codeList) == 0 {
		logrus.Warn("未获取到任何股票代码，跳过后续处理")
		return
	}

	// 3. 获取日期为 strTime 的所有股票日行情数据
	m := make(map[string]string)
	if m, err = getDailyMarketData(strTime, strings.Join(codeList, ",")); err != nil {
		logrus.Errorf("获取行情数据失败: %v", err)
		return
	}

	// 4. 把行情数据转换成 []model.DailyModel
	var dailyMarketList []qmt.DailyModel
	if dailyMarketList, err = GenerateDailyModelList(m); err != nil {
		logrus.Errorf("获取行情数据对象列表失败: %v", err)
		return
	}
	if err = updateDailyModelList(dailyMarketList); err != nil {
		logrus.Errorf("获取行情数据对象列表失败: %v", err)
		return
	}
}

// updateDailyModelList 更新数据到数据库中
func updateDailyModelList(dailyMarketList []qmt.DailyModel) error {
	if len(dailyMarketList) == 0 {
		return fmt.Errorf("updateDailyModelList: len(dailyMarketList) == 0")
	}
	rdbDaily := db.GetRedisDaily()
	ctx := context.Background()
	for _, dailyModel := range dailyMarketList {

		// 判断 key 是否存在
		exists, err := rdbDaily.Exists(ctx, constants.DailyKeyPrefix+dailyModel.StockCode).Result()
		if err != nil {
			logrus.WithField("股票代码", dailyModel.StockCode).Error("updateDailyModelList: rdb.Exists 判断 key 是否存在出现错误:", err)
			continue
		}
		if exists == 0 {
			logrus.WithField("股票代码", dailyModel.StockCode).Error("updateDailyModelList: rdb.Exists key 不存在")
			continue
		}

		if result, err := rdbDaily.ZPopMax(ctx, constants.DailyKeyPrefix+dailyModel.StockCode).Result(); err != nil {
			return fmt.Errorf("updateDailyModelList: ZPopMax：%v", err)
		} else {
			if bytes, err := convertor.ToBytes(result[0].Member); err != nil {
				logrus.Error(err)
				continue
			} else {
				var daily qmt.DailyModel
				if err := json.Unmarshal(bytes, &daily); err != nil {
					logrus.Error(err)
					continue
				}
				daily.Open = dailyModel.Open
				daily.High = dailyModel.High
				daily.Low = dailyModel.Low
				daily.Close = dailyModel.Close
				daily.Volume = dailyModel.Volume
				daily.Amount = dailyModel.Amount

				resp, err := utils.HttpGet(constants.MiniQMTBaseUrl+"/get_instrument_detail/"+dailyModel.StockCode, nil, nil)
				if err != nil {
					return fmt.Errorf("updateDailyModelList([DailyModel])-> HttpGet->get_instrument_detail 出现错误：%w", err)
				}
				defer func(Body io.ReadCloser) {
					_ = Body.Close()
				}(resp.Body)

				body, err := io.ReadAll(resp.Body)
				if err != nil {
					return fmt.Errorf("updateDailyModelList([DailyModel])-> io.ReadAll 出现错误：%w", err)
				}

				var instrumentDto dto.InstrumentDto
				if err = json.Unmarshal(body, &instrumentDto); err != nil {
					return fmt.Errorf("updateDailyModelList([DailyModel]) -> json.Unmarshal 出现错误：%w", err)
				}
				daily.StockName = instrumentDto.InstrumentName
				daily.DownStopPrice = mathutil.RoundToFloat(instrumentDto.DownStopPrice, constants.Precision2)
				daily.UpStopPrice = mathutil.RoundToFloat(instrumentDto.UpStopPrice, constants.Precision2)
				daily.FloatVolume = mathutil.RoundToFloat(instrumentDto.FloatVolume, constants.Precision2)
				daily.TotalVolume = mathutil.RoundToFloat(instrumentDto.TotalVolume, constants.Precision2)
				daily.FloatAmount = mathutil.RoundToFloat(instrumentDto.FloatVolume*daily.PreClose, constants.Precision2)
				daily.TotalAmount = mathutil.RoundToFloat(instrumentDto.TotalVolume*daily.PreClose, constants.Precision2)

				if daily.FloatAmount > 0 && daily.TotalAmount > 0 {
					daily.TurnoutRate = mathutil.RoundToFloat((daily.Volume*100/daily.FloatVolume)*100, constants.Precision2)
				} else {
					daily.TurnoutRate = 0
				}

				floatTime, err := convertor.ToFloat(daily.Time)
				if err != nil {
					return fmt.Errorf("updateDailyModelList -> convertor.ToFloat 时间转换失败：%w", err)
				}

				data, err := json.Marshal(&daily)
				if err != nil {
					return fmt.Errorf("updateDailyModelList -> json.Marshal 出现错误：%w", err)
				}

				result, err := rdbDaily.ZAdd(ctx, constants.DailyKeyPrefix+daily.StockCode, redis.Z{Score: floatTime, Member: data}).Result()
				if err != nil {
					return fmt.Errorf("updateDailyModelList -> Redis ZAdd 操作失败：%w", err)
				}
				_ = result // 如果不需要结果，保留 _ 即可
			}
		}
	}
	return nil
}
